home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Arsenal Files 6
/
The Arsenal Files 6 (Arsenal Computer).ISO
/
prg_basi
/
n_b-v200.zip
/
NB03
/
UNT
/
STR-MATH.UNT
< prev
next >
Wrap
Text File
|
1996-03-11
|
25KB
|
436 lines
$if 0
┌──────────────────────────╖ PowerBASIC v3.20
┌──┤ DASoft ╟──────────────────────┬──────────────────╖
│ ├──────────────────────────╢ Copyright 1995 │ DATE: 1996-01-01 ╟─╖
│ │ FILE NAME STR-MATH.UNT ║ by ╘════════════════─ ║ ║
│ │ LIBRARY DAS-NB03.PBL ║ Don Schullian, Jr. ║ ║
│ ╘══════════════════════════╝ ║ ║
│ A license is hereby granted to the holder to use this source code in ║ ║
│ any program, commercial or otherwise, without receiving the express ║ ║
│ permission of the copyright holder and without paying any royalties, ║ ║
│ as long as this code is not distributed in any compilable format. ║ ║
│ IE: source code files, PowerBASIC Unit files, and printed listings ║ ║
╘═╤═════════════════════════════════════════════════════════════════════╝ ║
│ ..................................... ║
╘═══════════════════════════════════════════════════════════════════════╝
$endif
'.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°
' ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° °
$if 1
$CODE SEG "DAS_NB03"
$EVENT OFF
$ERROR ALL OFF
$OPTIMIZE SPEED
$OPTION GOSUB OFF
$OPTION CNTLBREAK OFF
$OPTION SIGNED OFF
$DEBUG MAP OFF
$DEBUG PATH OFF
$DEBUG UNIT OFF
$COMPILE UNIT
$endif
'.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°
' ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° °
' PURPOSE: provide extended arithmetic functions for strings
' PARAMS: N1$ N1$ + N2$ or N1$ - N2$ or N1$ * N2$ or N1$ / N2$
' N2$ incoming numbers may be signed or not and pbvUsingChrs
' is used to determine which (if any) decimal point is used
' Decs% for DIVIDE only - the number of places past the decimal
' that are to be used in the answer
' RETURNS: the answer
' all values other than ZERO are signed with either + or -
' if the return value is ZERO then only a single "0" is returned
' NOTE: division by ZERO returns ZERO and not an error
' NOTE: N1$ * ".5" is faster than N1$ / "2"
'.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°.°
' ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° ° °
%F = 0 ' first pointer
%L = 1 ' last
%W = 2 ' working
%Pos = 43 ' plus sign
%Neg = 45 ' minus sign
DIM sN(2) AS SHARED STRING ' working strings
DIM sD(2,2) AS SHARED INTEGER ' sD%(0,X%) = places before decimal
' sD%(1,X%) = places past the decimal
' sD%(2,X%) = sign
DIM N_ptr(2,2) AS SHARED BYTE PTR ' N_ptr(%F,X%) = first digit
' N_ptr(%L,X%) = last digit
' N_ptr(%W,X%) = working digit
SHARED sD$, sA$, sZ$, sP$ ' dec$, ascii$, chr$(0), "."
' ──────────────────────────────────────────────────────────────────────────
FUNCTION fDIVnbr$( SEG N1$, SEG N2$, BYVAL Decs% ) LOCAL PUBLIC
LOCAL C%, L%, P%, X%
Format_NBRs N1$, N2$, ( 4 + Decs% ) ' get everybody ready
IF N_ptr(%F,0) = 0 THEN ' N1=0 or N2=0
FUNCTION = "0" ' function = 0
EXIT FUNCTION ' RETURN
END IF '
'
L% = LEN( sN$(2) ) ' length of divisor
IF sN$(1) < sN$(2) THEN INCR N_ptr(%F,0) ' if number > divisor
'
WHILE N_ptr(%F,0) =< N_ptr(%L,0) ' while still calculating
P% = L% ' P% = # of digits to use
IF sN$(1) < sN$(2) THEN INCR P% ' number is > divisor
IF @N_ptr(%F,0) = 46 THEN INCR N_ptr(%F,0) ' skip the decimal point
WHILE ( sN$(1) => sN$(2) ) OR ( P% > L% ) ' while N° > divisor
N_ptr(%W,1) = N_ptr(%F,1) + P% ' working pointers
N_ptr(%W,2) = N_ptr(%L,2) + 1 '
FOR X% = L% TO 1 STEP -1 ' do subtraction
DECR N_ptr(%W,1) ' decr pointers
DECR N_ptr(%W,2) '
IF ( C% > 0 ) THEN ' if carrying
IF @N_ptr(%W,1) > 0 THEN ' if digit > 0
DECR @N_ptr(%W,1) ' subtract carry amt
C% = 0 ' clear carry
ELSE ' else
@N_ptr(%W,1) = 9 ' carring a 9
END IF '
END IF '
IF @N_ptr(%W,2) > @N_ptr(%W,1) THEN ' if digit 1 > digit 2
C% = 10 ' carry 10
INCR @N_ptr(%W,1), C% ' bump digit 1
END IF '
DECR @N_ptr(%W,1), @N_ptr(%W,2) ' subtract d2 from d1
NEXT ' NEXT digit left
IF C% > 0 THEN ' if still carrying
DECR N_ptr(%W,1) ' prev digit
DECR @N_ptr(%W,1) ' decr ditit
C% = 0 ' clear carry
END IF '
IF (P% > L%) AND (ASCII( sN$(1) ) = 0) THEN ' check if right digit
P% = L% ' needs to fall off
MID$(sN$(1),1) = MID$( sN$(1),2) + sZ$ ' shift left
END IF '
INCR @N_ptr(%F,0) ' next digit in answer
WEND '
'
N_ptr(%W,1) = N_ptr(%L,1) ' check to see if number
FOR X% = LEN( sN$(1) ) TO 1 STEP -1 ' is now all ZERO's or
IF @N_ptr(%W,1) > 0 THEN EXIT FOR ' not
DECR N_ptr(%W,1) '
NEXT '
IF X% = 0 THEN EXIT LOOP ' Nope! - all done!
'
WHILE ASCII( sN$(1) ) = 0 ' if leading char ZERO
MID$( sN$(1), 1 ) = MID$(sN$(1),2) + sZ$ ' shift left
IF sN$(1) < sN$(2) THEN ' if number < divisor
INCR N_ptr(%F,0) ' skip digit in answer
IF @N_ptr(%F,0)=46 THEN INCR N_ptr(%F,0) ' skip decimal point
END IF '
WEND '
INCR N_ptr(%F,0) ' next digit in answer
WEND '
'
@N_ptr(%L,0) = 0 '
'
FUNCTION = fFormat_NBR$ ' clean-up & bail out!
END FUNCTION
' ──────────────────────────────────────────────────────────────────────────
FUNCTION fMULnbr$( SEG N1$, SEG N2$ ) LOCAL PUBLIC
LOCAL C%, D%, N1%, N2%, X%, Y%
Format_NBRs N1$, N2$, 3 ' get everybody ready
IF N_ptr(%F,0) = 0 THEN ' N1=0 or N2=0
FUNCTION = "0" ' function = 0
EXIT FUNCTION ' RETURN
END IF '
N1% = LEN( sN$(1) ) ' loop parameters
N2% = LEN( sN$(2) ) '
FOR X% = N1% TO 1 STEP -1 ' once for each dig in 1
IF @N_ptr(%L,1) = 0 THEN GOTO Mul_nbr ' digit = 0 skip it >> ┐
N_ptr(%W,0) = N_ptr(%L,0) ' end digit of working │
N_ptr(%W,2) = N_ptr(%L,2) ' end digit of 2 │
FOR Y% = N2% TO 1 STEP -1 ' each dig in 2 │
INCR C%, ( @N_ptr(%L,1) * @N_ptr(%W,2) ) ' inc carry, dig*dig │
INCR C%, @N_ptr(%W,0) ' inc carry, work dig │
@N_ptr(%W,0) = ( C% MOD 10 ) ' put right-most dig │
C% = ( C% \ 10 ) ' divide carry │
DECR N_ptr(%W,0) ' back-up digits │
DECR N_ptr(%W,2) ' │
IF @N_ptr(%W,0) = 46 THEN DECR N_ptr(%W,0) ' skip the decimal │
NEXT ' │
Mul_nbr: ' <<────────────────────┘
DECR N_ptr(%L,1) ' back-up end ptrs 1 & W
DECR N_ptr(%L,0) '
IF @N_ptr(%L,0) = 46 THEN DECR N_ptr(%L,0) ' skip decimal point
IF C% = 0 THEN ITERATE ' nothing to carry loop
INCR @N_ptr(%W,0), C% ' put the carry in W
C% = 0 ' clear carry
NEXT '
'
FUNCTION = fFormat_NBR$ ' clean-up & bail out!
END FUNCTION
' ──────────────────────────────────────────────────────────────────────────
FUNCTION fSUBnbr$( SEG N1$, SEG N2$ ) LOCAL PUBLIC
Format_NBRs N1$, N2$, 2 ' set-up the strings
IF N_ptr(%F,1) = 0 THEN ' N1 = 0
IF N_ptr(%F,2) = 0 THEN ' N2 = 0
FUNCTION = "0" ' FUNCTION = 0
EXIT FUNCTION '
END IF '
sN$(0) = sN$(2) ' N0 = N2
IF ABS(sD%(2,0)) = %Neg THEN ' swap sign
sD%(2,0) = %Pos '
ELSE '
sD%(2,0) = %Neg '
END IF '
ELSEif N_ptr(%F,2) = 0 THEN ' N2 = 0
sN$(0) = sN$(1) ' N0 = N1
END IF '
IF N_ptr(%F,0) = 0 THEN ' IF N1 = 0 OR N2 = 0
FUNCTION = fFormat_NBR$ ' format & exit
ELSEif sD%(2,0) < 0 THEN ' unlike signs (add)
FUNCTION = fADD_nbr$ ' call add
ELSE '
FUNCTION = fSUB_nbr$ ' do it!
END IF '
'
END FUNCTION '
' ──────────────────────────────────────────────────────────────────────────
FUNCTION fADDnbr$( SEG N1$, SEG N2$ ) LOCAL PUBLIC
Format_NBRs N1$, N2$, 1 ' format numbers
IF N_ptr(%F,1) = 0 THEN ' N1 = 0
IF N_ptr(%F,2) = 0 THEN ' N2 = 0
FUNCTION = "0" ' function = 0
EXIT FUNCTION '
END IF '
sN$(0) = sN$(2) ' N0 = N2
ELSEif N_ptr(%F,2) = 0 THEN ' N2 = 0
sN$(0) = sN$(1) ' N0 = N1
END IF '
IF N_ptr(%F,0) = 0 THEN ' N1=0 or N2=0
FUNCTION = fFormat_NBR$ ' format & exit
ELSEif ( sD%(2,0) < 0 ) THEN ' unlike signs
FUNCTION = fSUB_nbr$ ' subtract
ELSE '
FUNCTION = fADD_nbr$ ' do it!
END IF '
'
END FUNCTION '
' ==========================================================================
FUNCTION fADD_nbr$ () LOCAL PRIVATE
LOCAL N%, V%, Y%
FOR X% = LEN( sN$(0) ) TO 2 STEP -1
FOR Y% = 2 TO 0 STEP -1
DECR N_ptr(%W,Y%)
NEXT
IF @N_ptr(%W,0) = 46 THEN ITERATE ' decimal point
FOR N% = 2 TO 1 STEP -1
IF ( N_ptr(%W,N%) =< N_ptr(%L,N%) ) AND _
( N_ptr(%W,N%) => N_ptr(%F,N%) ) THEN INCR V%, @N_ptr(%W,N%)
NEXT
@N_ptr(%W,0) = ( V% MOD 10 )
V% = ( V% \ 10 )
NEXT
IF V% > 0 THEN
DECR N_ptr(%W,0)
@N_ptr(%W,0) = V%
END IF
FUNCTION = fFormat_NBR$
END FUNCTION
' ──────────────────────────────────────────────────────────────────────────
FUNCTION fSUB_nbr$ () LOCAL PRIVATE
LOCAL V1%, V2%, Y%, P%
LOCAL N1%, N2%, C%
IF ( sD%(0,2) > sD%(0,1) ) OR _ ' if N2 > N1
( sD%(0,2) = sD%(0,1) ) AND _ ' (checking for decimal
( sN$(2) > sN$(1) ) THEN ' point too)
N1% = 2 : N2% = 1 : sD%(2,0) = %Neg ' reverse number order
ELSE ' else
N1% = 1 : N2% = 2 ' normal order
END IF '
'
FOR P% = LEN( sN$(0) ) TO 1 STEP -1 '
FOR Y% = 2 TO 0 STEP -1 '
DECR N_ptr(%W,Y%) '
NEXT '
IF @N_ptr(%W,0) = 46 THEN ITERATE ' decimal point
IF N_ptr(%W,N1%) < N_ptr(%F,N1%) THEN ' past first of N1%
@N_ptr(%W,0) = C% ' put the carry value
EXIT FOR ' bail out of loop
END IF '
V1% = 0 : V2% = 0 '
IF N_ptr(%W,N1%) =< N_ptr(%L,N1%) THEN '
V1% = @N_ptr(%W,N1%) '
IF ( V1% > 0 ) AND ( C% > 0 ) THEN '
DECR V1% '
C% = 0 '
END IF '
END IF '
IF ( N_ptr(%W,N2%) =< N_ptr(%L,N2%) ) AND _ ' if #2 in range
( N_ptr(%W,N2%) => N_ptr(%F,N2%) ) THEN '
V2% = @N_ptr(%W,N2%) ' set value of #
END IF '
IF C% > 0 THEN ' carring 9's across
V1% = C% '
ELSEif ( V2% > V1% ) THEN ' if #2 > #1 then
C% = 9 ' carry 9's
INCR V1%, 10 ' bump #1 by 10
END IF '
@N_ptr(%W,0) = V1% - V2% ' do math & put answer
NEXT '
'
FUNCTION = fFormat_NBR$ ' format & be gone!
'
END FUNCTION '
' ──────────────────────────────────────────────────────────────────────────
SUB Format_NBRs ( SEG N1$, SEG N2$, BYVAL Which% ) LOCAL PRIVATE
LOCAL L%, N%, X%
sD$ = CHR$(0,1,2,3,4,5,6,7,8,9,44,46) ' decimal values
sA$ = "0123456789" + RIGHT$(pbvUSINGchrs,2) ' ascii values
sZ$ = CHR$(00) ' local strings
sP$ = CHR$(46) ' decimal point
sN$(1) = N1$ ' get into local vars
sN$(2) = N2$ '
FOR N% = 2 TO 0 STEP -1 '
IF N% > 0 THEN ' one of the numbers
sN$(N%) = LTRIM$( sN$(N%) ) ' strip off white space
sN$(N%) = EXTRACT$( sN$(N%), " " ) ' <ditto>
REPLACE ANY sA$ WITH sD$ IN sN$(N%) ' convert to decimals
sD%(2,N%) = ASCII( sN$(N%) ) ' looking for a sign
IF sD%(2,N%) <> %Neg THEN sD%(2,N%) = %Pos ' not neg so is positive
sN$(N%) = REMOVE$( sN$(N%), ANY " +-," ) ' strip our signs & junk
sN$(N%) = LTRIM$( sN$(N%), sZ$ ) ' get rid of leading 0's
IF INSTR( sN$(N%), sP$ ) > 0 THEN _ ' if a "." in string
sN$(N%) = RTRIM$( sN$(N%), ANY sP$+sZ$ ) ' strip trailing ".0"s
L% = LEN( sN$(N%) ) ' length of what's left
IF L% = 0 THEN ' oops! nothing there
IF N% = 2 THEN ITERATE ' if still 1 to go
IF N% = 1 THEN ' nothing left to do
sD%(2,0) = sD%(2,2) ' set final sign
EXIT SUB ' BYE BYE
END IF '
END IF '
X% = INSTR( sN$(N%), sP$ ) ' find the decimal point
IF X% = 0 THEN ' these variables
sD%(0,N%) = L% ' carry the number of
ELSEif X% = 1 THEN ' digits before and
sD%(1,N%) = L% - 1 ' after the decimal
ELSE ' there are in the
sD%(0,N%) = X% - 1 ' numbers
sD%(1,N%) = L% - X% '
END IF '
IF Which% < 3 THEN EXIT IF ' if ADD or SUB then exit
IF X% > 0 THEN ' if there is a dec.pnt
sN$(N%) = REMOVE$( sN$(N%), sP$ ) ' get rid of it
DECR L% ' adjust the length
END IF '
IF Which% = 3 THEN EXIT IF ' IF MULT then exit
IF X% = 1 AND ASCII( sN$(N%) ) = 0 THEN ' if number = .098 etc
X% = L% ' hold old length
sN$(N%) = LTRIM$( sN$(N%), sZ$ ) ' strip leading 0's
L% = LEN( sN$(N%) ) ' new length
IF N% = 1 THEN sD%(0,1) = -( X% - L% ) ' neg places before dec
END IF '
IF ( N% = 2 ) THEN EXIT IF ' if divisor then exit
X% = LEN( sN$(2) ) - L% + 1 ' pad N1 to length of N2
IF X% > 0 THEN _ ' + 1 so we've got room
sN$(1) = sN$(1) + STRING$(X%,0) ' to work with
ELSE ' ANSWER STRING
SELECT CASE Which% '
CASE < 3 ' ADD & SUB
sD%(0,N%) = MAX%(sD%(0,1),sD%(0,2))+1 ' longest whole # +1
sD%(1,N%) = MAX%(sD%(1,1),sD%(1,2)) ' longest decimal
CASE 3 ' MULT
sD%(0,N%) = ( sD%(0,1) + sD%(0,2) ) ' length of both wholes
sD%(1,N%) = ( sD%(1,1) + sD%(1,2) ) ' length of both decs
CASE ELSE ' DIVIDE
X% = LEN( sN$(2) ) - 1 ' length of divisor -1
sD%(0,N%) = sD%(0,1) + sD%(1,2) - X% ' whole of N1 and decs
IF sD%(0,N%) < 0 THEN ' N2 - len(N2) - 1
X% = sD%(0,N%) - 1 ' answer is < 1
sD%(0,N%) = 0 '
END IF '
sD%(1,N%) = Which% - 4 ' # of places after dec
END SELECT '
L% = sD%(0,N%) + sD%(1,N%) - (sD%(1,N%)>0) ' whole + decs + dec pnt
IF L% = 0 THEN EXIT SUB ' oops! nothing to do
sN$(0) = STRING$(L%,0) ' answer string all 0's
IF sD%(1,N%) > 0 THEN _ ' place the decimal pnt
MID$( sN$(0), sD%(0,N%)+1, 1 ) = sP$ ' if required
END IF '
' ───────────────────────────────────── '
N_ptr(%F,N%) = STRPTR32( sN$(N%) ) ' set first pointer
N_ptr(%L,N%) = N_ptr(%F,N%) + L% - 1 ' set last pointer
NEXT '
'
IF sD%(2,1) <> sD%(2,2) THEN ' check for unequal signs
sD%(2,0) = -sD%(2,1) ' return a negative value
ELSE ' equal signs
sD%(2,0) = sD%(2,1) ' return a positive value
END IF '
'
IF Which% > 2 THEN ' MULT & DIVIDE
IF sD%(2,0) < 0 THEN ' if unequal signs
sD%(2,0) = %Neg ' answer is negative
ELSE '
sD%(2,0) = %Pos ' answer is positive
END IF '
IF ( Which% > 3 ) AND _ ' DIVIDE and only decs.
( X% < 0 ) THEN DECR N_ptr(%F,0), X% ' jump dec pnt & places
ELSE '
INCR sD%(1,0) ' ADD & SUB
FOR N% = 2 TO 1 STEP -1 ' set ending pointers
IF ( sD%(1, 0) > 1 ) AND _ ' to max of either ptr
( sD%(1,N%) = 0 ) THEN DECR sD%(1,N%) ' so ADD & SUB can loop
N_ptr(%W,N%) = N_ptr(%L,N%) + _ ' an equal # of times
( sD%(1,0) - sD%(1,N%) ) ' for each number w/out
NEXT ' fouling the pointers
N_ptr(%W,0) = N_ptr(%L,0) + 1 ' working pointer
END IF '
'
END SUB
' ──────────────────────────────────────────────────────────────────────────
FUNCTION fFormat_NBR$ LOCAL PRIVATE
' FINAL CLEAN-UP
sN$(0) = LTRIM$( sN$(0), sZ$ ) ' strip leading ZERO's
IF INSTR( sN$(0), sP$ ) > 0 THEN ' if a decimal pnt there
sN$(0) = RTRIM$( sN$(0), sZ$ ) ' strip trailing ZERO's
sN$(0) = RTRIM$( sN$(0), sP$ ) ' strip trailing "."
END IF '
IF LEN( sN$(0) ) = 0 THEN ' if the answer is ZERO
FUNCTION = "0" ' RETURN plain ZERO
ELSE '
REPLACE ANY sD$ WITH sA$ IN sN$(0) ' switch back to ASCII
IF ASCII( sN$(0) ) = 46 THEN _ ' if leading decimal pnt
sN$(0) = "0"+sN$(0) ' tag with leading ZERO
FUNCTION = CHR$( ABS(sD%(2,0)) ) + sN$(0) ' concant sign & RETURN
END IF '
sD$ = "" ' clean-up shared
sA$ = "" ' strings &
sZ$ = "" ' arrays
sP$ = "" '
ERASE sD%, sN$, N_ptr '
END FUNCTION